#pragma once
#include <vector>
#include <map>
#include <string>
#include <utility>
//#include "FrameReader.h"

extern char currentProjectPath[300];

using namespace std;


extern char resultpath[200];
void errMsg(const char* str);
void write2Log(const char * str);


class DDAPoint
{
public:
	DDAPoint(double x, double y, int blockNo);
	DDAPoint() {}
public:
	double x;
	double y;
	double speedX;
	double speedY;
	int blockNo;
};

class FixedPoint : public DDAPoint
{
public:
	FixedPoint(double x, double y, int blockNo);
	FixedPoint();
};

class LoadingPoint : public DDAPoint
{
public:
	LoadingPoint(double x, double y, int blockNo);
	LoadingPoint();
};

class MeasuredPoint : public DDAPoint
{
public:
	MeasuredPoint(double x, double y, int blockNo);
	MeasuredPoint();
public:
	double u;
	double v;
	double r;
	double stressX;
	double stressY;
	double stressXY;
};

class BoltElement
{
public:
	pair<double, double> startPoint;
	pair<double, double> endPoint;
	double stiffness;
	double extension;
	double pretension;
};

class Block
{
public:
	vector< pair<double, double> > vertices;   // keep double precision
											   // each integer means a joint number for one vertice in vertices with the same subscript
	vector<int> verticesJoints;
	int blockIdx;
	int startNo;
	int endNo;
	double parameters[8];
	double stressX;
	double stressY;
	double stressXY;
};

class FrameData
{
	// TODO: how to identify empty (null) FrameData object
public:
	void reset();

public:
	int frameNo;
	vector<Block> blocks;
	vector<BoltElement> bolts;
	vector<FixedPoint> fixedPoints;
	vector<LoadingPoint> loadingPoints;
	vector<MeasuredPoint> measuredPoints;
	double totalTime;
	double internalTime;
	double maxDisplacement;
};
bool exportFrameData(FrameData& frame, string path);




class Joint
{
public:
	Joint() :fri(0), coh(0), ten(0) 
	{}
	Joint(double friction, double cohesion, double tension) 
		:fri(friction), coh(cohesion), ten(tension) 
	{}
public:
	/*friction*/
	double fri;
	/*cohesion*/
	double coh;
	/*tension*/
	double ten;
};
typedef std::vector<Joint> Joints;
typedef std::vector<std::string> Strings;

class JointManager
{
public:
	void updateJoints(double waterLevel, std::vector<std::vector<double> >& joints);

	std::map<std::string, Joint>& getJoints()
	{
		return _jnts;
	}
	std::map<double, Strings>& getJntConfigs()
	{
		return _jntsByWater;
	}
private:
	std::map<std::string, Joint> _jnts;
	std::map<double, Strings> _jntsByWater;
};

class WaterParameterManager
{
public:
	WaterParameterManager() :_curIdx(0) {}

	typedef std::vector<std::pair<double, double> > WaterLevels;
	WaterLevels& getLevels()
	{
		return _paras;
	}
	int getNumberOfLevel() const
	{
		return _paras.size();
	}
	bool needToUpdateLevel(double key) {
		return _curIdx<_paras.size() && _paras[_curIdx].first < key;
	}
	void updateLevel() {
		if(_curIdx<_paras.size()) {
			++_curIdx;
			printf("!!!!!!!!!! update parameter to < %lf , %lf > !!!!!! \n"
				, _paras[_curIdx].first, _paras[_curIdx].second);
		}
	}
	
	/**
	 * key may be step number or current time;
	 */
	double getCurrentLevel() {
		return _paras.size() == _curIdx ? _paras.back().second : _paras[_curIdx].second;
	}
	WaterLevels getWaterLevels()
	{
		return _paras;
	}
private:
	WaterLevels _paras;
	int _curIdx;
};

/**
 * @brief updateJntsByTime gives how to update joints, by time or step number?
 * @brief jntsNum gives the number of joints used in computing
 */
void loadJsonParas(const std::string& filename, JointManager& jntMgr, WaterParameterManager& wpMgr
	, bool& updateJntsByTime, int& jntsNum, int& savePerSteps);

